package org.cfan.seckill.configuration;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.TimeUnit;

/**
 * Created by lsq on 2017/7/10.
 */
@Aspect
@Component
public class CfanRequestLimitAspect {

    /**
     * Logger
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(CfanRequestLimitAspect.class);
    /**
     * 缓存前缀
     */
    private static final String PREFIX = "request_limit_";
    /**
     * RedisTemplate
     */
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 限制请求次数
     *
     * @param joinPoint
     * @param limit
     * @throws CfanRequestLimitAspect
     */
    @Before("within(@org.springframework.web.bind.annotation.RestController *) && @annotation(limit)")
    public void requestLimit(final JoinPoint joinPoint, CfanRequestLimit limit) throws Exception {

        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        if (request == null) {
            throw new Exception("请求异常");
        }
        //这里可以细化颗粒度做到支持多个客户端不同IP的访问
        String ip = getRemoteAddr(request);
        String url = request.getRequestURL().toString();
        String key = PREFIX.concat(url).concat(ip);

        long count = redisTemplate.opsForValue().increment(key, 1);
        long time = limit.time();
        if (count == 1) {
            if (null != limit.getParam() && "0000".equals(limit.getParam())) {
                time = 5;
            }
            redisTemplate.expire(key, time, TimeUnit.SECONDS);
        }
        if (count > limit.count()) {
            LOGGER.info("用户IP[" + ip + "]访问地址[" + url + "]超过了限定的次数[" + limit.count() + "]");
            if (null != limit.getParam() && "0000".equals(limit.getParam())) {
                throw new  Exception("请五秒后重试");
            }
            throw new Exception("HTTP请求超出设定的限制");
        }
    }
    /**
     * 获得用户远程地址
     *
     * @param request the request
     * @return the remote addr
     */
    public static String getRemoteAddr(HttpServletRequest request) {
        String remoteAddr = request.getHeader("X-Real-IP");
        if (isBlank(remoteAddr)) {
            remoteAddr = request.getHeader("X-Forwarded-For");
        }
        if (isBlank(remoteAddr)) {
            remoteAddr = request.getHeader("Proxy-Client-IP");
        }
        if (isBlank(remoteAddr)) {
            remoteAddr = request.getHeader("WL-Proxy-Client-IP");
        }
        return remoteAddr != null ? remoteAddr : request.getRemoteAddr();
    }
    /**
     * 检验是否为空或空字符串
     *
     * @param str
     * @return
     */
    public static boolean isBlank(String str) {
        return isNull(str).equals("");
    }
    /**
     * 字符串空处理，去除首尾空格 如果str为null，返回"",否则返回str
     *
     * @param str
     * @return
     */
    public static String isNull(String str) {
        if (str == null) {
            return "";
        }
        return str.trim();
    }
}
